package com.wqnmdb.im.annotation;

import com.alibaba.fastjson.JSON;
import com.wqnmdb.im.domain.contants.ConstantEnum;
import com.wqnmdb.im.domain.resp.RespUtils;
import com.wqnmdb.im.service.redis.RedisStringService;
import com.wqnmdb.im.utils.DateUtil;
import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.util.ByteUtils;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 请求及响应数据日志打印
 */
@Slf4j
@Aspect
@Order(1)
@Component
public class PrintDataAspect {

    @Pointcut("@annotation(com.wqnmdb.im.annotation.PrintData)")
    public void print() {
    }

    /**
     * @param point
     */
    @Around("print()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        //获取注解
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        PrintData printData = method.getAnnotation(PrintData.class);
        //目标类、方法
        String className = StringUtils.isNotBlank(printData.className()) ?
                printData.className() : method.getDeclaringClass().getName();
        String methodName = StringUtils.isNotBlank(printData.methodName()) ?
                printData.methodName() : method.getName();
        List<Object> args = new ArrayList<>();

        Arrays.asList(point.getArgs()).forEach(arg -> {
            if (arg instanceof Errors || arg instanceof MultipartFile) {
                return;
            }
            args.add(arg);
        });
        log.info("类名：{}.方法名：{}.请求报文大小：{}KB.请求报文内容：{}.", className, methodName, getByteSize(args),JSON.toJSONString(args));

        //执行方法
        Object object = point.proceed();

        log.info("类名：{}.方法名：{}.响应报文大小：{}KB.响应报文内容：{}.", className, methodName, getByteSize(object),JSON.toJSONString(object));
        //整理施工现场
        return object;
    }

    /**
     * 计算数据占用内存大小
     * @param data
     * @return
     */
    private double getByteSize(Object data){
        long byteSize = 0;
        try {
            byteSize = ObjectSizeCalculator.getObjectSize(data);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            return Double.valueOf(String.format("%.2f", (double) byteSize / 1024));
        }
    }
}